package com.kermel.andromeda.data;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import android.content.Context;
import android.net.Uri;
import android.os.Parcel;
import android.provider.Settings;
import android.test.AndroidTestCase;

import com.kermel.common.util.StringUtils;

public class AlarmTest extends AndroidTestCase {
    public static final String NAME_FIXTURE = "NameFixture";
    public static final boolean ENABLED_TRUE_FIXTURE = true;
    public static final boolean ENABLED_FALSE_FIXTURE = false;
    public static final boolean VIBRATE_TRUE_FIXTURE = true;
    public static final boolean VIBRATE_FALSE_FIXTURE = false;
    public static final long TRIGGER_TIME_FIXTURE = 123456789;
    public static final UUID ID_FIXTURE = UUID.randomUUID();
    public static final String RINGTONE_URI_STRING_FIXTURE = "RingtoneUriStringFixture";
    
    private Alarm alarm;
    
    protected void setUp() throws Exception {
        super.setUp();
        
        alarm = createAlarmFixture();
    }

    protected void tearDown() throws Exception {
        alarm = null;
        
        super.tearDown();
    }
    
    public static Alarm createAlarmFixture() {
        Alarm alarmFixture = new Alarm();
        
        alarmFixture.enabled = ENABLED_TRUE_FIXTURE;
        alarmFixture.name = NAME_FIXTURE;
        alarmFixture.triggerTime = TRIGGER_TIME_FIXTURE;
        alarmFixture.vibrate = VIBRATE_TRUE_FIXTURE;
        alarmFixture.uuid = ID_FIXTURE;
        alarmFixture.ringtoneUriString = RINGTONE_URI_STRING_FIXTURE;
        
        return alarmFixture;
    }
    
    public static Alarm createCopyOfAlarm(Alarm otherAlarm) {
        if (otherAlarm == null)
            throw new IllegalArgumentException("Given alarm cannot be null.");
        
        Alarm alarm = createAlarmFixture();
        alarm.enabled = otherAlarm.enabled;
        alarm.name = otherAlarm.name;
        alarm.ringtoneUriString = otherAlarm.ringtoneUriString;
        alarm.triggerTime = otherAlarm.triggerTime;
        alarm.vibrate = otherAlarm.vibrate;
        
        UUID otherUUID = otherAlarm.uuid;
        if (otherUUID != null) {
            long mostSigBits = otherUUID.getMostSignificantBits();
            long leastSigBits = otherUUID.getLeastSignificantBits();
            alarm.uuid = new UUID(mostSigBits, leastSigBits);
        }
        
        return alarm;
    }
    
    public void testThatNameDefaultsToEmptyString() {
        Alarm alarmFixture = new  Alarm();
        assertEquals(StringUtils.EMPTY, alarmFixture.name);
    }
    
    public void testGettingSettingName() {
        alarm.name = NAME_FIXTURE;
        
        assertEquals(NAME_FIXTURE, alarm.name);
    }
    
    public void testEnabledDefaultsToTrue() {
        Alarm alarmFixture = new  Alarm();
        assertTrue(alarmFixture.enabled);
    }
    
    public void testVibrateDefaultsToTrue() {
        Alarm alarmFixture = new  Alarm();
        assertTrue(alarmFixture.vibrate);
    }
    
    public void testParcelWriteAndRead() {
        Parcel parcel = Parcel.obtain();
        Alarm alarmFixture = createAlarmFixture();
        int flags = 0;
        
        alarmFixture.writeToParcel(parcel, flags);
        
        parcel.setDataPosition(0);
        
        Alarm alarmFromParcel = new Alarm(parcel);
        
        assertTrue(alarmFromParcel.enabled);
        assertEquals(ID_FIXTURE, alarmFromParcel.uuid);
        assertEquals(NAME_FIXTURE, alarmFromParcel.name);
        assertEquals(TRIGGER_TIME_FIXTURE, alarmFromParcel.triggerTime);
        assertTrue(alarmFromParcel.vibrate);
    }
    
    public void testEqualsReturnsFalseForNullInput() {
        Alarm alarmFixture = createAlarmFixture();
        
        boolean result = alarmFixture.equals(null);
        assertFalse(result);
    }
    
    public void testEqualsReturnsFalseWhenInputIsNotAnAlarm() {
        Alarm alarmFixture = createAlarmFixture();
        String stringFixture = StringUtils.SPACE;
        
        boolean result = alarmFixture.equals(stringFixture);
        assertFalse(result);
    }
    
    public void testThatEqualsReturnsFalseWhenAlarmsAreDifferent() {
        Alarm alarmFixture = createAlarmFixture();
        alarmFixture.name = "Alarm.name.1";
        
        Alarm theOtherAlarm = createAlarmFixture();
        alarmFixture.name = "Alarm.name.2";
        
        boolean result = alarmFixture.equals(theOtherAlarm);
        assertFalse(result);
    }
    
    public void testThatEqualsReturnsTrueWhenAlarmsAreSame() {
        Alarm alarmFixture = createAlarmFixture();
        alarmFixture.name = "Alarm.name.1";
        
        boolean result = alarmFixture.equals(alarmFixture);
        assertTrue(result);
    }
    
    public void testThatHashCodeReturnsSameValueForSameInstance() {
        Alarm alarmFixture = createAlarmFixture();
        
        int firstHashValue = alarmFixture.hashCode();
        int secondHashValue = alarmFixture.hashCode();
        
        assertEquals(firstHashValue, secondHashValue);
    }
    
    public void testThatHashCodeReturnsDifferentValueForDifferentInstances() {
        Alarm firstAlarmFixture = createAlarmFixture();
        Alarm secondAlarmFixture = createAlarmFixture();
        
        int firstHashValue = firstAlarmFixture.hashCode();
        int secondHashValue = secondAlarmFixture.hashCode();
        
        assertFalse(firstHashValue == secondHashValue);
    }
    
    public void testUseOfHashCodeWhenAlarmIsUsedInMap() {
        Alarm firstAlarmFixture = createAlarmFixture();
        Alarm secondAlarmFixture = createAlarmFixture();
        
        Map<Alarm, Integer> alarmMap = new HashMap<Alarm, Integer>();
        int firstAlarmOrdering = 1;
        int secondAlarmOrdering = 2;
        
        alarmMap.put(firstAlarmFixture, firstAlarmOrdering);
        alarmMap.put(secondAlarmFixture, secondAlarmOrdering);
        
        int firstResult = alarmMap.get(firstAlarmFixture);
        assertEquals(firstAlarmOrdering, firstResult);
        
        int secondResult = alarmMap.get(secondAlarmFixture);
        assertEquals(secondAlarmOrdering, secondResult);
    }
    
    public void testThatAlarmsAreEqualUsingCreateCopyOfAlarm() {
        Alarm firstAlarmFixture = createAlarmFixture();
        Alarm secondAlarmFixture = createCopyOfAlarm(firstAlarmFixture);
        assertEquals(firstAlarmFixture, secondAlarmFixture);
    }
    
    public void testThatEqualsReturnsFalseWhenAlarmsHaveDifferentNames() {
        Alarm firstAlarmFixture = createAlarmFixture();
        Alarm secondAlarmFixture = createCopyOfAlarm(firstAlarmFixture);
        
        secondAlarmFixture.name = "SecondAlarmFixture";
        assertFalse(firstAlarmFixture.equals(secondAlarmFixture));
    }
    
    public void testThatEqualsReturnsFalseWhenAlarmsHaveDifferentEnabledValues() {
        Alarm firstAlarmFixture = createAlarmFixture();
        Alarm secondAlarmFixture = createCopyOfAlarm(firstAlarmFixture);

        secondAlarmFixture.enabled = !firstAlarmFixture.enabled;
        assertFalse(firstAlarmFixture.equals(secondAlarmFixture));
    }
    
    public void testThatEqualsReturnsFalseWhenAlarmsHaveDifferentRingtoneUriStrings() {
        Alarm firstAlarmFixture = createAlarmFixture();
        Alarm secondAlarmFixture = createCopyOfAlarm(firstAlarmFixture);

        secondAlarmFixture.ringtoneUriString = "SecondAlarmRingtoneUriString";
        assertFalse(firstAlarmFixture.equals(secondAlarmFixture));
    }
    
    public void testThatEqualsReturnsFalseWhenAlarmsHaveDifferentTriggerTimes() {
        Alarm firstAlarmFixture = createAlarmFixture();
        Alarm secondAlarmFixture = createCopyOfAlarm(firstAlarmFixture);
        
        secondAlarmFixture.triggerTime = 1;
        assertFalse(firstAlarmFixture.equals(secondAlarmFixture));
    }
    
    public void testThatEqualsReturnsFalseWhenAlarmsHaveDifferentVibrateValues() {
        Alarm firstAlarmFixture = createAlarmFixture();
        Alarm secondAlarmFixture = createCopyOfAlarm(firstAlarmFixture);

        secondAlarmFixture.vibrate = !firstAlarmFixture.vibrate;
        assertFalse(firstAlarmFixture.equals(secondAlarmFixture));
    }
    
    public void testThatEqualsReturnsFalseWhenAlarmsHaveDifferentUUIDs() {
        Alarm firstAlarmFixture = createAlarmFixture();
        Alarm secondAlarmFixture = createCopyOfAlarm(firstAlarmFixture);
        
        secondAlarmFixture.uuid = UUID.randomUUID();
        assertFalse(firstAlarmFixture.equals(secondAlarmFixture));
    }
    
    public void testThatAcceptRingtoneDoesNotThrowExceptionWhenRingtoneUriIsNull() {
        boolean exceptionCaught = false;
        
        try {
            Alarm alarm = createAlarmFixture();
            Context context = getContext();
            Uri ringtoneUri = null;
            alarm.acceptRingtone(context, ringtoneUri);
        } catch (Exception e) {
            exceptionCaught = true;
        }
        
        assertFalse(exceptionCaught);
    }
    
    public void testThatAcceptRingtoneSetsAlarmRingtoneUriStringAndTitle() {
        Alarm alarm = createAlarmFixture();
        Context context = getContext();
        Uri ringtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
        
        alarm.acceptRingtone(context, ringtoneUri);

        assertTrue(StringUtils.isNotBlank(alarm.ringtoneUriString));
        assertTrue(StringUtils.isNotBlank(alarm.ringtoneTitle));
    }
    
    public void testClearRingtone() {
        Alarm alarm = createAlarmFixture();
        Context context = getContext();
        Uri ringtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
        
        alarm.acceptRingtone(context, ringtoneUri);
        
        alarm.clearRingtone();
        
        assertTrue(StringUtils.isEmpty(alarm.ringtoneUriString));
        assertTrue(StringUtils.isEmpty(alarm.ringtoneTitle));
    }
    
    public void testHasRingtoneReturnsTrueWhenRingtoneHasBeenSet() {
        Alarm alarm = createAlarmFixture();
        Context context = getContext();
        Uri ringtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
        
        alarm.acceptRingtone(context, ringtoneUri);
        
        assertTrue(alarm.hasRingtone());
    }
    
    public void testHasRingtoneReturnsFalseWhenNoRingtoneHasBeenSet() {
        Alarm alarm = createAlarmFixture();
        
        alarm.clearRingtone();
        
        assertFalse(alarm.hasRingtone());
    }
    
}
